package models.games;

import java.util.Random;

import models.util.Indexes;
import core.User;

public class MemorizzaTabella extends Game {

	private boolean matrix[][];
	private int dimension;// default
	private int trueTiles;
	private Random random = new Random();
	private Indexes[] trueTilesIndexes;

	// score startingLevel requiredLevel currentLevel area

	public MemorizzaTabella(User user) {
		super(user);
		setLevel(0/* DataBaseManager.getLastScore(user, this)/100 */); // DA
																		// RIVEDERE
		refresh(false);
	}

	/**
	 * Calcola la dimensione della matrice a partire dal livello corrente che
	 * può assumere valori da 0 a 5. Al livello zero si ha una matrice 2x2. Se
	 * level > 5 la dimensione resta invariata.
	 * 
	 * @param level
	 *            : livello corrente di gioco
	 * @return la dimensione della matrice matrix.
	 */
	private int calculateDimension() {
		int l = getLevel();
		return 2 + l;
	}

	public boolean[][] getMatrix() {
		return matrix;
	}

	public Indexes[] getTrueTiles() {
		return trueTilesIndexes;
	}

	public int getDimension() {
		return dimension;
	}

	/**
	 * Calcola il numero di casella da porre a true aggiungendo alla dimensione
	 * il doppio del livello corrente.
	 * 
	 * @return il numero di casella da porre a true
	 */
	private int trueTilesCardinality() {
		return dimension + getLevel() * 2;
	}

	/**
	 * Aggiorna la matrice matrix ricreandola sulla base dello stato attuale del
	 * gioco.
	 */
	public void refresh(boolean won) {
		if (won) {
			setScore(score + (200 * (level + 1)));
			if (level < 5)
				level += 1;
		} else {
			if (level > 0) {
				level -= 1;
			}
		}
		matrix = null;
		dimension = calculateDimension();
		trueTiles = trueTilesCardinality();
		matrix = new boolean[dimension][dimension];
		initialize();
	}

	/**
	 * Inizializza la matrice ponendo a true un numero di caselle pari a
	 * trueTiles. La scelta delle celle da porre a true avviene in maniera
	 * random. In un array di Indexes "cartesianProduct" si tiene traccia di
	 * tutte le possibili coppie di indici. Si sceglie poi una coppia di indici
	 * da questo array secondo la seguente logica: tramite un oggetto Random,
	 * che può generare un intero pari al massimo a dim, si ricava un'indice x
	 * di cartesianProduct in corrispondenza del quale si trova l'oggetto
	 * Indexes, con la coppia scelta, che verr� salvato su un altro array;
	 * successivamente si scambia l' x-esimo elemento di cartesianProduct con
	 * l'utimo e si decrementa la variabile dim in modo da escludere di volta in
	 * volta la possilità che l'oggetto Random generi un indice pari all'ultima
	 * cella di cartesianProduct. In questo modo si elimina il rischio di
	 * prendere pi� volte uno stesso oggetto Indexes.
	 */
	private void initialize() {
		int dim = dimension * dimension;
		Indexes[] cartesianProduct = new Indexes[dim];
		int n = 0;
		for (int i = 0; i < dimension; i++)
			for (int j = 0; j < dimension; j++)
				cartesianProduct[n++] = new Indexes(i, j);
		trueTilesIndexes = new Indexes[trueTiles];
		for (int i = 0; i < trueTiles; i++) {
			int x = random.nextInt(dim);
			trueTilesIndexes[i] = cartesianProduct[x];
			cartesianProduct[x] = cartesianProduct[dim - 1];
			cartesianProduct[dim - 1] = null;
			dim--;
		}
		for (int k = 0; k < trueTiles; k++)
			matrix[trueTilesIndexes[k].getI()][trueTilesIndexes[k].getJ()] = true;
	}

	/**
	 * Verifica se la cella i-j della matrice è true.
	 * 
	 * @param i
	 *            : riga della matrice
	 * @param j
	 *            : colonna della matrice
	 * @return il valore della cella i-j
	 */
	public boolean verify(int i, int j) {
		if (i < 0 || j < 0 || i >= dimension || j >= dimension)
			throw new ArrayIndexOutOfBoundsException();
		return matrix[i][j];
	}

	@Override
	public int getGameKey() {
		return 3;
	}

}
